package edu.chalmers.sdx;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Arrays;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

import android.opengl.GLU;
import android.util.Log;

public class Crosshair {
	private float[] vertices;
	private FloatBuffer vertexBuffer; // Buffer for vertex-array
	
	private float[] modelViewMatrix = new float[16];
	private float[] projectionMatrix = new float[16];
	private int[] viewport = new int[4];
	
	public static int RAY_ITERATIONS = 100;
	public static float COLLISION_RADIUS = 100f;
	
	public Crosshair(){
		vertices = new float[]{
				0, 10, 0,
				0, -10, 0,
				10, 0, 0,
				-10, 0, 0
		};
		
		// Setup vertex-array buffer. Vertices in float. A float has 4 bytes
		ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
		vbb.order(ByteOrder.nativeOrder()); // Use native byte order
		vertexBuffer = vbb.asFloatBuffer(); // Convert from byte to float
		vertexBuffer.put(vertices); // Copy data into buffer
		vertexBuffer.position(0); // Rewind
	}
	
	// Draw the shape
	public void draw(GL10 gl) {
		gl.glFrontFace(GL10.GL_CCW); // Front face in counter-clockwise orientation
		gl.glEnable(GL10.GL_CULL_FACE); // Enable cull face
		gl.glCullFace(GL10.GL_BACK); // Cull the back face (don't display)

		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

		// Set the color of the crosshair
		gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
		
		// Draw the primitive from the vertex-array directly
		gl.glDrawArrays(GL10.GL_LINES, 0, 4);
		gl.glLineWidth(5);

		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisable(GL10.GL_CULL_FACE);
	}
	
	public void checkCollision(GL10 gl, float modelX, float modelY, float modelZ) {
		gl.glGetIntegerv(GL11.GL_VIEWPORT,viewport,0);
		((GL11)gl).glGetFloatv(GL11.GL_MODELVIEW_MATRIX, modelViewMatrix, 0);
		((GL11)gl).glGetFloatv(GL11.GL_PROJECTION_MATRIX, projectionMatrix, 0);
		
		float[] nearPoint = {0.0f, 0.0f, 0.0f, 0.0f};
		float[] farPoint  = {0.0f, 0.0f, 0.0f, 0.0f};
		float[] rayVector = {0.0f, 0.0f, 0.0f};

		// Retreiving position projected on near plane
		GLU.gluUnProject(viewport[2] / 2, viewport[3] - viewport[3] / 2, -1.0f, modelViewMatrix, 0,
				projectionMatrix, 0, viewport, 0, nearPoint, 0);

		// Retreiving position projected on far plane
		GLU.gluUnProject(viewport[2] / 2, viewport[3] - viewport[3] / 2, 100.0f, modelViewMatrix, 0,
				projectionMatrix, 0, viewport, 0, farPoint, 0);
		
//		Log.d("Matrix", "ModelView: " + Arrays.toString(modelViewMatrix));
//		Log.d("Matrix", "Projection: " + Arrays.toString(projectionMatrix));
//		Log.d("Matrix", "Viewport: " + Arrays.toString(viewport));

		// extract 3d Coordinates put of 4d Coordinates
		nearPoint = fixW(nearPoint);
		farPoint = fixW(farPoint);

//		Log.d("Crosshair", "Near point: " + Arrays.toString(nearPoint));
//		Log.d("Crosshair", "Far point: " + Arrays.toString(farPoint));
		
		// Processing ray vector
		rayVector[0] = farPoint[0] - nearPoint[0];
		rayVector[1] = farPoint[1] - nearPoint[1];
		rayVector[2] = farPoint[2] - nearPoint[2];

		// calculate ray vector length
		float rayLength = (float) Math
				.sqrt((rayVector[0] * rayVector[0])
						+ (rayVector[1] * rayVector[1])
						+ (rayVector[2] * rayVector[2]));

//		Log.d("Crosshair", "Ray length: " + rayLength);
		// normalizing ray vector
		rayVector[0] /= rayLength;
		rayVector[1] /= rayLength;
		rayVector[2] /= rayLength;

//		Log.d("Crosshair", "Ray Vector: " + Arrays.toString(rayVector));
		
		float[] collisionPoint = {0.0f, 0.0f, 0.0f};
		float[] objectCenter   = {-160, 0, 0};
		
		//Iterating over ray vector to check for collisions
		for(int i = 0; i < RAY_ITERATIONS; i++){
			collisionPoint[0] = rayVector[0] * rayLength/RAY_ITERATIONS * i;
			collisionPoint[1] = rayVector[1] * rayLength/RAY_ITERATIONS * i;
			collisionPoint[2] = rayVector[2] * rayLength/RAY_ITERATIONS * i;

			if (pointSphereCollision(collisionPoint, objectCenter, COLLISION_RADIUS)){
				Log.d("Collision", "We have a collision at " + Arrays.toString(collisionPoint));
			}
		}
	}
	
	/**
	 * Convert the 4D input into 3D space (or something like that, otherwise the
	 * gluUnproject values are incorrect)
	 * 
	 * @param v 4D input
	 * @return 3D output
	 * @author http://stackoverflow.com/users/1029225/mh
	 */
	private float[] fixW(float[] v) {
		float w = v[3];
		for (int i = 0; i < 4; i++)
			v[i] = v[i] / w;
		return v;
	}

	/**
	 * check if a given point in space collides with a given object center with
	 * a given radius based on ios code from:
	 * http://blog.nova-box.com/2010/05/iphone-ray-picking-glunproject-sample.html
	 * 
	 * @param point point to check for collision with object x,y,z
	 * @param center center of the object x,y,z
	 * @param radius
	 * @return true on collision, false on no collision
	 */
	private boolean pointSphereCollision(float[] point, float[] center,
			float radius) {
		
		float crazyShit = (float)(Math.pow(point[0] - center[0], 2)
				+ Math.pow(point[1] - center[1], 2)
				+ Math.pow(point[2] - center[2], 2));
		float radiusSquare = radius * radius;
		
//		Log.d("CollisionCheck", "Crazy shit: " + crazyShit + ", Radius^2: " + radiusSquare);
//		Log.d("CollisionCheck", "Crazy shit: " + (Math.pow(point[0] - center[0], 2)
//				+ Math.pow(point[1] - center[1], 2)
//				+ Math.pow(point[2] - center[2], 2)) / 10 + ", Radius^2: " + Math.pow(radius, 2));

		return(crazyShit < radiusSquare);
//		return ((point[0] - center[0]) * (point[0] - center[0])
//				+ (point[1] - center[1]) * (point[1] - center[1])
//				+ (point[2] - center[2]) * (point[2] - center[2]) / 10 < (radius * radius));
	}
}
